package org.internna.expensetracker.model;

import java.util.List;
import java.util.Set;
import java.util.HashSet;
import javax.persistence.FetchType;
import javax.persistence.NamedQueries;
import javax.persistence.OneToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQuery;
import javax.persistence.CascadeType;
import javax.validation.constraints.NotNull;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.roo.addon.entity.RooEntity;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.serializable.RooSerializable;
import org.internna.expensetracker.model.security.UserDetails;

@RooEntity
@RooJavaBean
@RooSerializable
@NamedQueries({
	@NamedQuery(name = Subcategory.FIND_SELECTABLE_CATEGORIES, query = "SELECT c FROM Subcategory c WHERE owner = :owner ORDER BY id"), 
	@NamedQuery(name = Subcategory.FIND_EXPENSE_CATEGORIES, query = "SELECT c FROM Subcategory c WHERE c.parentCategory.income = false AND owner = :owner ORDER BY id")
})
public class Subcategory implements Comparable<Subcategory> {

	private static final long serialVersionUID = 6271890094038605150L;

	public static final String FIND_EXPENSE_CATEGORIES = "findExpenses";

    public static final String FIND_SELECTABLE_CATEGORIES = "filterTransfers";

    private static final Log logger = LogFactory.getLog(Subcategory.class);

    @NotNull
    private String category;

    @NotNull
    @ManyToOne
    private Category parentCategory;

    @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, mappedBy = "subcategory")
    private Set<AccountTransaction> transactions = new HashSet<AccountTransaction>();

    @NotNull
    @ManyToOne(fetch = FetchType.LAZY)
    private UserDetails owner;

    @SuppressWarnings("unchecked")
    public static final List<Subcategory> findExpenseCategories(UserDetails user) {
        return filterTransfers(entityManager().createNamedQuery(FIND_EXPENSE_CATEGORIES).setParameter("owner", user).getResultList(), 3);
    }

    @SuppressWarnings("unchecked")
    public static final List<Subcategory> findAllSubcategories(UserDetails user) {
    	return entityManager().createNamedQuery(FIND_SELECTABLE_CATEGORIES).setParameter("owner", user).getResultList();
    }

    @SuppressWarnings("unchecked")
    public static final List<Subcategory> findSelectableCategories(UserDetails user) {
        return filterTransfers(findAllSubcategories(user), 7);
    }

    public static final Subcategory findBySubcategory(String category, UserDetails user) {
        try {
            return entityManager().createQuery("select c from Subcategory c where c.category = :category AND owner = :owner", Subcategory.class)
            		.setParameter("owner", user).setParameter("category", category).getSingleResult();
        } catch (RuntimeException re) {
            logger.warn("Subcategory [" + category + "] not found in database (" + re.getMessage() + ")");
            return null;
        }
    }

    public boolean isUnique() {
        return category.equals(parentCategory.getCategory());
    }

    public final Boolean isIncome() {
        return parentCategory == null ? null : parentCategory.isIncome();
    }

    public static final Subcategory createInstance(String key, Long parent, UserDetails user) {
    	return createInstance(key, Category.findCategory(parent), user);
    }

    public static final Subcategory createInstance(String key, Category parent, UserDetails user) {
    	Subcategory subcategory = new Subcategory();
    	subcategory.setOwner(user);
    	subcategory.setCategory(key);
    	subcategory.setParentCategory(parent);
    	subcategory.persist();
    	return subcategory;
    }

    private static List<Subcategory> filterTransfers(List<Subcategory> data, int remove) {
    	return data != null && data.size() > remove ? data.subList(remove, data.size()) : data;
    }

    @Override public int compareTo(Subcategory other) {
        return other == null ? -1 : parentCategory.compareTo(other.parentCategory) == 0 ? category.compareTo(other.category) : parentCategory.compareTo(other.parentCategory);
    }

    @Override public final String toString() {
        return getCategory();
    }
}
